배경지식
- 리눅스 소켓 라이브러리는 Berkeley Sockets (BSD Sockets) 표준을 따름
sys/socket.h에 라이브러리 정의
- 소켓 통신은 IP protocol suite에서, Transport 계층에 해당 (TCP/UDP)
- TCP의 패킷은
세그먼트, UDP의 패킷은데이터그램
- TCP / UDP 별도의 소켓 존재
- 소켓통신은 전이중 지원
sys/socket.h→ syscall → net stack → NIC driver 순서로 처리
- 코드에서
Listen Socket,Accept Socket둘 로 나뉨
다시보는 IP protocol suite

왜 TCP를 연결 지향이라고 하는가?
- handshake로 연결을 설정 - 연결 보장
- seq 시퀀스 번호 존재 - 순서 보장
TCP State
TCP FSM

TCP FSM을 참조하면, Handshake 등의 과정이 한 눈에 보인다.
TCP FSM은 커널의 TCP 스택이 내부적으로 구현하고 관리하며, 소켓 사용자는 위 FSM을 참고하여 코딩하면 된다.
연결 시도, 3-way handshake
Client Server | -------- SYN ------------> | | | | <------- SYN + ACK ------- | | | | -------- ACK ------------> | | |
연결 성립 (ESTABLISHED 상태)
Estabilished
Client Server | --- data(seq=x+1) --------> | | | | <--- ack(ack=x+1+len) ----- | | ... | | <--- data(seq=y+1) -------- | | --- ack(ack=y+1+len) ----> |
연결 해제, 4-way handshake
Client Server | ------- FIN ------------> | | | | <------ ACK ------------- | | | | <------ FIN ------------- | | ------- ACK ------------> |
그림에서는 클라이언트가 FIN을 보내는 것으로 되어있지만, 클라이언트, 서버 양측에서 모두 Close가 가능하다.
Close 요청을 보내는 쪽
- FIN-WAIT-1
- FIN을 보내고 ACK를 기다리는 상태
- FIN-WAIT-2
- ACK를 받은 뒤, FIN을 받기를 기다리는 상태
- CLOSING
- 양 측에서 모두 FIN을 동시에 보냈을 때 상태
- TIME-WAIT
- 연결을 닫은 뒤에도, 임시 시간(리눅스 기본 60s)동안 작동하지 않는 소켓을 유지한다. 이유는 후술
Close를 닫는 쪽
- CLOSE-WAIT
- FIN 요청에 대한 ACK를 보냈고, FIN을 다시 보냄
- LAST-ACK
- 보낸 FIN에 대한 ACK를 받음
소켓 통신 과정

- 많이 하는 오해 : 서버의 로컬 포트는 accept 될 때 마다 새로 할당된다? - X
- 당연히 서버의 로컬 포트는 최초에 서버에서
socket()으로 열었을 때 생성된 것이 전부이다.
Listen Socket vs Established Socket
- Listen Socket →
socket()으로 얻음
socket->state = LISTENING- Established Socket (connected socket 등)
socket->state = ESTABLISHEDServer-Side 소켓 코드
socket()
socket(domain, type, protocol) 옵션
- domain
AF_INET: IPv4 네트워크AF_INET6: IPv6 네트워크AF_UNIX|AF_LOCAL: IPC
- type
SOCK_STREAM: TCPSOCK_DGRAM: UDP
- protocol
0으로 자동선택
Listen 소켓의 File Descriptor를 얻는다. 서버 사이드의 이 소켓은 LISTEN 소켓이고, 실질적으로는 이후
accept() 함수의 결과로 나온 FD로 통신한다.STATE:
CLOSEDbind()
IP와 포트 설정
STATE :
CLOSEDlisten()
오픈 대기큐 (backlog)를 준비
STATE :
LISTENaccept()
Client-Side에서
connect() 가 진행되었을 때 나타난다.backlog 큐에 들어간 연결을 꺼내 새 연결 소켓 FD 반환, 해당 소켓으로 통신이 일어난다.
STATE :
ESTABLISHED write()
데이터를 보낸다. 한 번에 최대 ssize_t 만큼 보낼 수 있다. write()로 보낸 데이터는 통신 소켓 버퍼로 보내지고, 이후 TCP network stack에서 패킷 별로 쪼개진 뒤 보내진다.
read()
데이터를 받는다. 블로킹으로, 필요한 데이터가 버퍼로 모두 들어올 때 까지 대기한다.
recv()
read()는 File Descripter를 이용한 것이지만, 실제로는 recv()를 이용하는 것이 더 좋다.
Client-Side 소켓 코드
connect()
3-way handshake의 진행
STATE :
ESTABLISHED (성공시)bind()
클라이언트 사이드에서
bind() 를 생략다중 연결과 Backlog
Established상태인 연결을 대기시키는 큐인Backlog존재
accept
fork() 를 이용해 멀티 프로세싱에서 처리하기
int srv = socket(...); bind(srv, ...); listen(srv, 128); while (1) { int conn = accept(srv, NULL, NULL); if (fork() == 0) { // 자식 프로세스: 연결 하나 처리 handle_client(conn); close(conn); exit(0); } // 부모 프로세스: 다음 연결 대기 close(conn); // 부모는 자식이 맡았으니 FD 닫음 }
TIME_WAIT 문제?
- TCP는 연결지향이므로, 통신이 끝난 이후 연결을 끊음
- 연결 종료 시 지연 패킷으로 인한 혼란을 막기 위해, 원격 종단의 연결을 막기
- 결론적으로 클라이언트의 포트가 고갈되지 않는 이상, 서버 측에서는 TIME_WAIT로 인한 오버헤드를 걱정할 필요는 없다. 문제가 되었던 것은 과거 메모리 자원량이 적었던 시절의 이야기
File 구조
struct file struct socket